#include "tunnel_mainwindow.h"
#include "ui_tunnel_mainwindow.h"
#include <time.h>
#include <QtGui/QMessageBox>


void addFriendToList(QStandardItemModel *model, const QString nickName, bool online, const QString status)
{
    model->insertRow(0);
    model->setData(model->index(0, 0), nickName);
    model->setData(model->index(0, 1), status);
}

Tunnel_MainWindow::Tunnel_MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::Tunnel_MainWindow)
{
    ui->setupUi(this);

    socket = new ClientSocket(this);


    // External dialogs and frames
    addFriendDialog = new AddFriendDialog(this);
    connect( addFriendDialog, SIGNAL(addNewFriend(QString,QString)), this, SLOT(addNewFriend(QString,QString)));


    // Privatechat manager
    privateChatManager = new PrivateChatManager(this);
    connect( privateChatManager, SIGNAL(sendMessageToFriend(const QString&, const QString&)),
             this, SLOT(on_sendMessageToFriend(const QString&, const QString&)) );


    // Connect Socket with the GUI.
    connect( socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)) );
    connect( socket, SIGNAL(disconnected()), this, SLOT(on_socketDisconnected()) );
    connect( socket, SIGNAL(cSocketError(QString)), this, SLOT(on_cSocketError(QString)) );
    connect( socket, SIGNAL(changeLoginStatus(int, QString)), this, SLOT(on_changeLoginStatus(int, QString)) );
    connect( socket, SIGNAL(successfullyLoggedIn()), this, SLOT(on_successfullyLoggedIn()) );
    connect( socket, SIGNAL(loginFailed(QString)), this, SLOT(on_loginFailed(QString)) );


    // Connect Chathandler with GUI
    connect( socket->getChatHandler(), SIGNAL(newGlobalMessage(QString, QString)), this, SLOT(on_newGlobalMessage(QString, QString)) );
    connect( socket->getChatHandler(), SIGNAL(newPrivateMessage(QString, QString, QString)), privateChatManager, SLOT(on_newPrivateMessage(QString, QString, QString)) );
    connect( socket->getChatHandler(), SIGNAL(cError(QString)), this, SLOT(on_cSocketError(const QString&)) );
    connect( socket->getChatHandler(), SIGNAL(updateFriendlist(QString)), this, SLOT(on_updateFriendlist(QString)));
    connect( socket->getChatHandler(), SIGNAL(incommingFriendrequest(QString)), this, SLOT(on_incommingFriendrequest(QString)) );
    connect( socket->getChatHandler(), SIGNAL(signal_friendGoesOnline(QString)), this, SLOT(on_friendGoesOnline(QString)) );
    connect( socket->getChatHandler(), SIGNAL(signal_friendGoesOffline(QString)), this, SLOT(on_friendGoesOffline(QString)) );


    // Other connections
    connect( ui->lePasswort, SIGNAL(returnPressed()), this, SLOT(on_lePasswort_returnPressed()) );
    connect( ui->leBenutzername, SIGNAL(returnPressed()), this, SLOT(on_lePasswort_returnPressed()) );
    connect( ui->teMessage, SIGNAL(returnWithoutShiftPressed()), this, SLOT(on_teMessage_returnWithoutShiftPressed()) );
    connect( ui->action_Beenden, SIGNAL(triggered()), qApp, SLOT(quit()) );


    // Friendlist
    friendlistItemModel = new QStandardItemModel(0, 2, ui->lvFriends);
    friendlistItemModel->setHeaderData(0, Qt::Horizontal, tr("Name"));
    friendlistItemModel->setHeaderData(1, Qt::Horizontal, tr("Status"));

    ui->lvFriends->setModel(friendlistItemModel);
    ui->lvFriends->setContextMenuPolicy(Qt::CustomContextMenu);


    // Friendlist contextmenu
    friendlistMenu = new QMenu(ui->lvFriends);
    friendlistMenu->addMenu("TEST");


    // SystemTrayIcon
    restoreAction = new QAction(tr("&Restore"), this);
    connect( restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()) );

    quitAction = new QAction(tr("&Quit"), this);
    connect( quitAction, SIGNAL(triggered()), qApp, SLOT(quit()) );

    sysTrayIconMenu = new QMenu(this);
    sysTrayIconMenu->addSeparator();
    sysTrayIconMenu->addAction(restoreAction);
    sysTrayIconMenu->addAction(quitAction);

    sysTrayIcon = new QSystemTrayIcon(this);
    sysTrayIcon->setContextMenu(sysTrayIconMenu);

    // Connect sysTrayIcon events
    connect( sysTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(sysTrayIconActivated(QSystemTrayIcon::ActivationReason)) );

    QIcon icon(":/resources/icon/logo_v3_32x32.png");
    sysTrayIcon->setIcon(icon);

    sysTrayIcon->show();
}

Tunnel_MainWindow::~Tunnel_MainWindow()
{
    // Delete friendrequest windows
    for( int i = 0; i < friendRequests.size(); ++i )
    {
        delete friendRequests[i];
    }

    delete socket;
    delete privateChatManager;
    delete addFriendDialog;
    delete friendlistMenu;
    delete sysTrayIconMenu;
    delete sysTrayIcon;
    delete ui;
}

void Tunnel_MainWindow::on_pbLogin_clicked()
{
    QString error = "";
    if(ui->leBenutzername->text() == "")
        error += "Please enter a valid username!\n";

    if(ui->lePasswort->text() == "")
        error += "Please enter a valid password!\n";

    if( error != "" )
    {
        QMessageBox::information(this, tr("Tunnel - Error"), error);
        return;
    }

    // Lock Tunnel GUI
    ui->tabWidget->setEnabled(false);

    on_changeLoginStatus(10, "Try to connect to the server...");

    // Setup login information, password should be encryptet in future!
    socket->setLoginInformation(ui->leBenutzername->text().toStdString().c_str(),
                                ui->lePasswort->text().toStdString().c_str());

    // Logout ( if logged in )
    on_pbLogout_clicked();

    // Reconnect
    socket->connectToHost("127.0.0.1", 9001);
}

void Tunnel_MainWindow::displayError(QAbstractSocket::SocketError socketError)
{
    switch (socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        QMessageBox::information(this, tr("Tunnel - Error"),
                                 tr("Could not find host, verify that you are connected to the internet!"));
        break;
    case QAbstractSocket::ConnectionRefusedError:
        QMessageBox::information(this, tr("Tunnel - Error"),
                                 tr("Connection to Tunnel Server failed! Please try again later."));
        break;
    default:
        QMessageBox::information(this, tr("Tunnel"),
                                 tr("The following error occurred: %1.")
                                 .arg(socket->errorString()));
    }
    // Unlock Tunnel GUI
    ui->tabWidget->setEnabled(true);

    ui->pbLogout->setEnabled(false);
    on_changeLoginStatus(0, "Disconnected from the server!");
    ui->tabWidget->setCurrentIndex(0);
}

void Tunnel_MainWindow::on_socketDisconnected()
{
    // Remove friends from friendlist
    friendlistItemModel->removeRows(0, friendlistItemModel->rowCount());
    on_changeLoginStatus(0, "Lost connection to the server!");
}

void Tunnel_MainWindow::on_newGlobalMessage(const QString &from, const QString &message)
{
    time_t t;
    struct tm *ts;

    t = time(NULL);
    ts = localtime(&t);
    
    QString newLine = QString("[%1%2:%3%4] %5: %6").arg(ts->tm_hour < 10 ? "0" : "").arg(ts->tm_hour).
                                                    arg(ts->tm_min < 10 ? "0" : "").arg(ts->tm_min).
                                                    arg(from).
                                                    arg(message);
    
    ui->tbChat->appendPlainText(newLine);
}

void Tunnel_MainWindow::on_cSocketError(const QString &error)
{
    QMessageBox::information(this, tr("Tunnel - Error"), error);
}

void Tunnel_MainWindow::on_pbSenden_clicked()
{
    Package package;

    setupMessagePackage(&package, ui->teMessage->toPlainText());
    socket->sendPackage(&package);

    ui->teMessage->setPlainText("");
}

void Tunnel_MainWindow::on_teMessage_returnWithoutShiftPressed()
{
    on_pbSenden_clicked();
}

void Tunnel_MainWindow::on_action_Beenden_activated()
{
    close();
}

void Tunnel_MainWindow::on_leBenutzername_returnPressed()
{
    on_pbLogin_clicked();
}

void Tunnel_MainWindow::on_lePasswort_returnPressed()
{
    on_pbLogin_clicked();
}

void Tunnel_MainWindow::on_updateFriendlist(QString sFriendlist)
{
    // Remove friends from friendlist
    friendlistItemModel->removeRows(0, friendlistItemModel->rowCount());

    while( sFriendlist.length() > 1 )
    {
        // Extracting friends data from incomming friendlist
        QString myFriend  = sFriendlist.left( sFriendlist.indexOf(';') );

        QString loginName = myFriend.left( myFriend.indexOf(':') ); // Should popup as tooltip, unused up to now

        QString nickName  = myFriend.mid( myFriend.indexOf(':') + 1,
                                          myFriend.indexOf(':', myFriend.indexOf(':')) );

        QString status    = myFriend.mid( myFriend.indexOf(':', myFriend.indexOf(':') + 1) + 1 );
        bool online       = ( status == "online" ? true : false );

        // Remove friends data from incomming friendlist
        sFriendlist = sFriendlist.right( sFriendlist.length() - sFriendlist.indexOf(';') - 1 );

        addFriendToList(friendlistItemModel, nickName, online, status);
    }
}

void Tunnel_MainWindow::on_pbNewFriend_clicked()
{
    addFriendDialog->show();
}

void Tunnel_MainWindow::addNewFriend(const QString &friend_loginName, const QString &rMessage)
{
    Package package;
    setupSystemPackage(&package, TUNNEL_COMMANDCATEGORY_FRIENDS, TUNNEL_COMMAND_ADDFRIEND, friend_loginName + ":" + rMessage);
    socket->sendPackage(&package);

    QMessageBox::information(this, tr("Tunnel - Friends"),
                             tr("Your friendship request was sended to %1.").arg(friend_loginName));
}

void Tunnel_MainWindow::on_incommingFriendrequest(QString sFriendrequests)
{
    while( sFriendrequests.length() > 1 )
    {
        // Extracting friends data from incomming friendlist
        QString friendrequest  = sFriendrequests.left( sFriendrequests.indexOf(';') );

        quint32 uid       = friendrequest.left( friendrequest.indexOf(':') ).toULongLong(); // Should popup as tooltip, unused up to now

        QString nickName  = friendrequest.mid( friendrequest.indexOf(':') + 1,
                                               friendrequest.indexOf(':', friendrequest.indexOf(':') + 1) - 2 );

        QString rMessage  = friendrequest.mid( friendrequest.indexOf(':', friendrequest.indexOf(':') + 1) + 1 );

        // Remove friends data from incomming friendlist
        sFriendrequests = sFriendrequests.right( sFriendrequests.length() - sFriendrequests.indexOf(';') - 1 );

        // Create a new window for this request.
        friendRequests.resize(friendRequests.size() + 1);
        friendRequests.at(friendRequests.size() - 1) = new FriendInventationDialog(uid, nickName, rMessage);
        connect( friendRequests.at(friendRequests.size() - 1), SIGNAL(acceptFriendInventation(const quint32&)), this, SLOT(acceptFriendInventation(const quint32&)) );
        friendRequests.at(friendRequests.size() - 1)->show();
    }
}

void Tunnel_MainWindow::acceptFriendInventation(const quint32 &applicant_uid)
{
    QString arguments;
    arguments.sprintf("%d", applicant_uid);
    Package package;
    setupSystemPackage(&package, TUNNEL_COMMANDCATEGORY_FRIENDS, TUNNEL_COMMAND_ACCEPTFRIENDINVENTATION, arguments);
    socket->sendPackage(&package);

    // Refresh friendlist <--- Critical, will cost network traffic, should change this...
    Package friendlistPackage;
    setupSystemPackage(&friendlistPackage, TUNNEL_COMMANDCATEGORY_FRIENDS, TUNNEL_COMMAND_UPDATEFRIENDLIST);
    socket->sendPackage(&friendlistPackage);
}

void Tunnel_MainWindow::on_changeLoginStatus(int progress, const QString &status)
{
    ui->pbarStatus->setValue(progress);
    ui->lStatus->setText(status);
}

void Tunnel_MainWindow::on_lvFriends_customContextMenuRequested(QPoint pos)
{
    friendlistMenu->exec(ui->lvFriends->mapToGlobal(pos));
}

void Tunnel_MainWindow::on_successfullyLoggedIn()
{
    // Unlock Tunnel GUI
    ui->tabWidget->setEnabled(true);

    ui->pbLogout->setEnabled(true);

    ui->tabWidget->setCurrentIndex(1);
}

void Tunnel_MainWindow::on_loginFailed(const QString &reason)
{
    QMessageBox::information(this, tr("Tunnel - Login Failed"), reason);

    // Unlock Tunnel GUI
    ui->tabWidget->setEnabled(true);

    ui->tabWidget->setCurrentIndex(0);
}

void Tunnel_MainWindow::on_pbLogout_clicked()
{
    if( socket->isOpen() )
        socket->abort();

    ui->pbLogout->setEnabled(false);
}

void Tunnel_MainWindow::on_friendGoesOnline(const QString &friend_loginName)
{
    showSysTrayMessage(friend_loginName+" logged in.", 3);
    // Have to change the friendlist, and not reorder it from the server, due to network traffic, will do this on a later development state.
    Package friendlistPackage;
    setupSystemPackage(&friendlistPackage, TUNNEL_COMMANDCATEGORY_FRIENDS, TUNNEL_COMMAND_UPDATEFRIENDLIST);
    socket->sendPackage(&friendlistPackage);
}

void Tunnel_MainWindow::on_friendGoesOffline(const QString &friend_loginName)
{
    showSysTrayMessage(friend_loginName+" logged off.", 3);
    // Have to change the friendlist, and not reorder it from the server, due to network traffic, will do this on a later development state.
    Package friendlistPackage;
    setupSystemPackage(&friendlistPackage, TUNNEL_COMMANDCATEGORY_FRIENDS, TUNNEL_COMMAND_UPDATEFRIENDLIST);
    socket->sendPackage(&friendlistPackage);
}

void Tunnel_MainWindow::sysTrayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
    switch(reason)
    {
    case QSystemTrayIcon::Trigger:
        break;
    case QSystemTrayIcon::DoubleClick: // Should bring main window to front if this is the case...
        showNormal();
        QApplication::setActiveWindow(this);
        break;
    case QSystemTrayIcon::MiddleClick:
        break;
    default:
        ;
    }
}

void Tunnel_MainWindow::closeEvent(QCloseEvent *event)
{
    event->ignore();
    hide();
}

void Tunnel_MainWindow::changeEvent(QEvent *event)
{
    if( event->type() == QEvent::WindowStateChange )
    {
        if( isMinimized() )
        {
            // Should minimize the window to the systray, will do this on a later state of development.
        }
    }

    QMainWindow::changeEvent(event);
}

void Tunnel_MainWindow::showSysTrayMessage(const QString &message, int duration, const QString &title, int icon)
{
    sysTrayIcon->showMessage(title, message, QSystemTrayIcon::MessageIcon(icon), duration * 1000);
}

void Tunnel_MainWindow::on_lvFriends_doubleClicked(QModelIndex index)
{
    privateChatManager->newPrivateChat(index.data().toString());
}

void Tunnel_MainWindow::on_sendMessageToFriend(const QString &friend_loginName, const QString &message)
{
    Package package;

    setupMessagePackage(&package, message, friend_loginName);
    socket->sendPackage(&package);
}
